#include "Logger.h"

#include <unistd.h>
#include <ctime>
#include <iostream>
#include <sstream>

#include "ConfigurationFile.h"
#include <BaseException.h>

Logger* Logger::instance = NULL;

const std::string DEFAULT_PATH = "/tmp/ConcuStation.log";
const int DEFAULT_LEVEL = Logger::LogLevel::DEBUG;
const std::string DEFAULT_LEVEL_NAME = "DEBUG";

std::string Logger::pathToLogfile = DEFAULT_PATH;
int Logger::loggingLevel = DEFAULT_LEVEL;

Logger::Logger() {
	init();
	lockLogfile.abrir(pathToLogfile);
}

Logger* Logger::getInstance() {
	if (Logger::instance == NULL)
		Logger::instance = new Logger();
	return Logger::instance;
}

Logger::~Logger() {
	lockLogfile.cerrar();
}

std::string Logger::getTime() {
	
	time_t rawtime;
	struct tm * timeinfo;
	char buffer[50];
	
	time(&rawtime);
	timeinfo = localtime(&rawtime);
	
	strftime(buffer, 50, "%d/%m/%Y %H:%M:%S - ", timeinfo);
	
	return std::string(buffer);
}

void Logger::init() {
	try {
		std::string levelStr = ConfigurationFile::getProperty(
			"LOGGER_LEVEL");
		bool validLevel = false;
		if (levelStr == "DEBUG") {
			loggingLevel = Logger::LogLevel::DEBUG;
			validLevel = true;
		}
		if (levelStr == "INFO") {
			loggingLevel = Logger::LogLevel::INFO;
			validLevel = true;
		}
		if (levelStr == "WARN") {
			loggingLevel = Logger::LogLevel::WARN;
			validLevel = true;
		}
		if (levelStr == "ERROR") {
			loggingLevel = Logger::LogLevel::ERROR;
			validLevel = true;
		}
		if (!validLevel) {
			loggingLevel = DEFAULT_LEVEL;
			throw BaseException(
				"No se pudo obtener el nivel de logueo correctamente. Se tomara el maximo nivel de logueo: " + DEFAULT_LEVEL_NAME);
		}
	} catch (const BaseException& e) {
		std::cout << "Logger:: " << e.what() << std::endl;
	}
	
	try {
		pathToLogfile = ConfigurationFile::getProperty(
			"LOGGER_FILEPATH");
		if(pathToLogfile.empty()){
			pathToLogfile = DEFAULT_PATH;
			throw BaseException(
				"No se pudo obtener la direccion donde se guardaran los logs. Se tomara el path: " + DEFAULT_PATH);
		}
	} catch (const BaseException& e) {
		std::cout << "Logger:: " << e.what() << std::endl;
	}
}

void Logger::destroy() {
	if (instance != NULL) {
		delete instance;
		instance = NULL;
	}
}

void Logger::debug(std::string message) {
	int key = Logger::LogLevel::DEBUG;
	Logger::insert(key, message);
}

void Logger::info(std::string message) {
	int key = Logger::LogLevel::INFO;
	Logger::insert(key, message);
}

void Logger::warn(std::string message) {
	int key = Logger::LogLevel::WARN;
	Logger::insert(key, message);
}

void Logger::error(std::string message) {
	int key = Logger::LogLevel::ERROR;
	Logger::insert(key, message);
}

void Logger::insert(int key, std::string value) {
	std::stringstream message;
	
	std::string logLevel;
	switch(key){
		case Logger::LogLevel::DEBUG:
			logLevel = "DEBUG";
			break;
		case Logger::LogLevel::INFO:
			logLevel = "INFO";
			break;
		case Logger::LogLevel::WARN:
			logLevel = "WARN";
			break;
		case Logger::LogLevel::ERROR:
			logLevel = "ERROR";
			break;
	}
	
	lockLogfile.tomarLock();
	
	if (key <= loggingLevel)
		message << Logger::getTime() << "[" << logLevel << ":" << getpid() << "] "
		<< value << std::endl;
	
	std::string messageStr = message.str();
	lockLogfile.escribir(static_cast<const void*>(messageStr.c_str()),
						 messageStr.size());
	
	lockLogfile.liberarLock();
}

